/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package yoga.algo.polygon;

import java.awt.Polygon;
import java.awt.Rectangle;
import yoga.algo.phenotype.IFitnessCalculator;

public class PolygonFitnessCalc implements IFitnessCalculator<Polygon>
{
    private double circularity = 0;
    private double distanceFromReference = 0;
    private Polygon polygonReference;

    public PolygonFitnessCalc(Polygon polygonReference)
    {
        this.polygonReference = polygonReference;
    }

    @Override
    public String getCalculatorName()
    {
        return "Polygon Fitness Calculator";
    }

    @Override
    public String getDescription()
    {
        return "Calculates the fitness of polygon by using Circularity, Roundness, Average distance between vertices and center of mass and Area differences from original shape";
    }

    @Override
    public double getFitnessValue(Polygon polygon)
    {
        updatePolygonStatistics(polygon);
        double w = 3;
        double fitValue = (this.circularity) - (w * this.distanceFromReference);
        if (fitValue < 0)
        {
            fitValue = 0;
        }

        return fitValue;
    }

    @Override
    public double getNeutralOrZeroValue()
    {
        return 0;
    }

    private void updatePolygonStatistics(Polygon polygon)
    {

        if (polygon.npoints > 0)
        {
            int xm = 0;
            int ym = 0;
            double deltaX = 0;
            double deltaY = 0;
            double accumalatedRadiousesDistanceFromCenter = 0;
            double accumalatedRadiousesDistanceFromPolygon = 0;

            //Calculate center of bounding box
            Rectangle rect = polygon.getBounds();
            xm = (int) (rect.x + (rect.width / 2));
            ym = (int) (rect.y + (rect.height / 2));

            //Calculate radiouses from center to each coordinate, calculate sum of distances
            double radiousArrayCenter[] = new double[polygon.npoints];
            double radiousArrayPolygon[] = new double[polygon.npoints];

            for (int i = 0; i < polygon.npoints; i++)
            {
                double sideX = (xm - polygon.xpoints[i]) * (xm - polygon.xpoints[i]);
                double sideY = (ym - polygon.ypoints[i]) * (ym - polygon.ypoints[i]);
                radiousArrayCenter[i] = Math.sqrt(sideX + sideY);
                accumalatedRadiousesDistanceFromCenter += radiousArrayCenter[i];

                sideX = (polygonReference.xpoints[i] - polygon.xpoints[i]) * (polygonReference.xpoints[i] - polygon.xpoints[i]);
                sideY = (polygonReference.ypoints[i] - polygon.ypoints[i]) * (polygonReference.ypoints[i] - polygon.ypoints[i]);
                radiousArrayPolygon[i] = Math.sqrt(sideX + sideY);
                accumalatedRadiousesDistanceFromPolygon += radiousArrayPolygon[i];
            }

            double averageRadiousCenter = accumalatedRadiousesDistanceFromCenter / polygon.npoints;
            double averageDistancePolygon = accumalatedRadiousesDistanceFromPolygon / polygon.npoints;

            //Calculate distance from center
            double accumalatedRadiousesSRS = 0;
            for (int i = 0; i < polygon.npoints; i++)
            {
                accumalatedRadiousesSRS += Math.abs(radiousArrayCenter[i] - averageRadiousCenter);
            }

            double roundnessRatio = 1 - (accumalatedRadiousesSRS / (averageRadiousCenter * polygon.npoints));
            this.circularity = roundnessRatio;

            //Calculate distance from polygon
            accumalatedRadiousesSRS = 0;
            for (int i = 0; i < polygon.npoints; i++)
            {
                accumalatedRadiousesSRS += Math.abs(radiousArrayPolygon[i] - averageDistancePolygon);
            }

            double distanceFromReferenceRatio = (accumalatedRadiousesSRS / (averageDistancePolygon * polygon.npoints));
            this.distanceFromReference = distanceFromReferenceRatio;
        }
        else
        {
            this.circularity = 0;
            this.distanceFromReference = 1;
            throw new RuntimeException("Polygon has 0 coordinates");
        }

    }

    @Override
    public double getMaximumValue()
    {
        return Double.MAX_VALUE;
    }
}
